
import numpy as np
from Comparison_Algorithm.Multiclass_Knapsack  import calculate_buffer,choose_bitrate_level,QoE,value
from Comparison_Algorithm.Bola import decide_m,calculate_Parameter

MAX_Q = 3 # 动态去找吧
MIN_Q = 2

class Env_Alg():
    metadata = {
        'render.modes': ['human', 'rgb_array'],
        'video.frames_per_second' : 50
    }

    def __init__(self,state_rate_mapping,FoV_Adjacent):

        self.video = np.array([0,1,2,3,4,5]) # 2*2 tiled  不懂 video如何
        self.state = [0,0,0,0,0,0,0] # * 2 #
        self.ep_steps = 0 #
        self.video_slot = 0
        self.record_bitratelevel = []
        self.tile_adjacent_relation = {0:[2,3,4,5],
                                       1:[2,3,4,5],
                                       2:[0,1,4,5],
                                       3:[0,1,4,5],
                                       4:[0,1,2,3],
                                       5:[0,1,2,3]}  # tile与其他tile的关联程度，是自行设计的
        self.action = [0,0,0,0,0,0]
        self.buffer = [0,0,0,0,0,0]  # 每一个tile有一个buffer
        self.last_bitrate = [0,0,0,0,0,0] # [[0,0,0,0]] # 4个tile 1个时隙  上一时刻的比特率
        self.rebuffer_event = [] # rebuffer事件
        self.throughput = []
######## Bola
        self.step = 0
        self.slot = [0,0,0,0,0,0]
        self.Q = [0,0,0,0,0,0]
        self.V,self.rp = calculate_Parameter()

        self.state_rate_mapping = state_rate_mapping
                            # {0: 8,
                            #   1: 24,
                            #   2: 36,
                            #   3: 48} # 单位MBps

        self.FoV_Adjacent = FoV_Adjacent
                        # {0:[2,3,4,5],
                        # 1:[2,3,4,5],
                        # 2:[0,1,4,5],
                        # 3:[0,1,4,5],
                        # 4:[0,1,2,3],
                        # 5:[0,1,2,3]}
        self.before_action_buffer = [0,0,0,0,0,0]
        self.max_buffer_size = 10.5

    def Highest_Bitrate_Selection(self, FoV, throughput,alpha,beta,gamma,artificial_main,artificial_main_near,artificial_not_main,artificial_duimian): # bitrate_factor
        reward = 0
        action = 3
        rebuffer_event = 0
        rebuffer_flag = 0
        rebuffer_record_time = 0
        for i in range(6):
            if self.buffer[i] < 1:
                self.buffer[i] += throughput[i] / self.state_rate_mapping[action]
                if self.buffer[i] < 1:
                    rebuffer_event += 1 - self.buffer[i]
                    rebuffer_flag = 1
                    rebuffer_record_time += 1
            else:
                self.buffer[i] = 1 + throughput[i] / self.state_rate_mapping[action]

        if not rebuffer_flag:
            self.buffer = (np.array(self.buffer) - 1).tolist()

        self.buffer = np.array(self.buffer)
        self.buffer[self.buffer > self.max_buffer_size] = self.max_buffer_size
        self.buffer = self.buffer.tolist()

        FoV_inside_bitrate_each_tile = []
        FoV_adjacency_bitrate_each_tile = []
        FoV_outside_bitrate_each_tile = []
        artificial_range_1s = {}
        range_temp = [0,1,2,3,4,5]
        main_perspective = 0
        main_near = []
        for i in range_temp:
            if FoV[self.video_slot + 1][i].astype(int) == 1:
                # count = list(FoV[self.video_slot + 1].astype(int)).count(1)
                artificial_range_1s[i] = artificial_main  # 主视野内权重为0.8
                FoV_inside_bitrate_each_tile.append(action)
                range_temp.remove(i)
                main_perspective = i
                for j in self.FoV_Adjacent[i]:
                    range_temp.remove(j)
                    if FoV[self.video_slot + 1][j].astype(int) == 1:
                        artificial_range_1s[j] = artificial_main_near# 0.5
                        FoV_adjacency_bitrate_each_tile.append(action)
                        main_near.append(j)
                    else:
                        artificial_range_1s[j] = artificial_not_main# 0.2
                        FoV_adjacency_bitrate_each_tile.append(action)
        artificial_range_1s[range_temp[0]] = artificial_duimian ## ???
        FoV_outside_bitrate_each_tile.append(action)

        bitrate_each_tile_weight = []
        for i in range(6):  # 6个面
            bitrate_each_tile_weight.append((action+1) * artificial_range_1s[i])
        bitrate_level_total = np.sum(bitrate_each_tile_weight)
        # 帧内平滑度
        # 考虑FoV的东西，这个平滑度考虑主视角和其他四片的差异
        smoothness_within_frame = 0
        smoothness_inside_FoV = 0
        smoothness_adjacency_FoV = 0
        smoothness_outside_FoV = 0


        bitrate = 0
        for i in range(6):
            bitrate += action

        reward = alpha * bitrate_level_total - beta * smoothness_within_frame - gamma * rebuffer_event
        return [reward,bitrate_level_total, smoothness_within_frame, rebuffer_event,rebuffer_record_time,
                FoV_inside_bitrate_each_tile,FoV_adjacency_bitrate_each_tile,FoV_outside_bitrate_each_tile,
                smoothness_inside_FoV,smoothness_adjacency_FoV,smoothness_outside_FoV]

    def Lowest_Bitrate_Selection(self, FoV, throughput,alpha,beta,gamma,artificial_main,artificial_main_near,artificial_not_main,artificial_duimian): # bitrate_factor
        reward = 0
        action = 0
        rebuffer_event = 0
        rebuffer_flag = 0
        rebuffer_record_time = 0
        for i in range(6):
            if self.buffer[i] < 1:
                self.buffer[i] += throughput[i] / self.state_rate_mapping[action]
                if self.buffer[i] < 1:
                    rebuffer_event += 1 - self.buffer[i]
                    rebuffer_flag = 1
                    rebuffer_record_time += 1
            else:
                self.buffer[i] = 1 + throughput[i] / self.state_rate_mapping[action]
        if not rebuffer_flag:
            self.buffer = (np.array(self.buffer) - 1).tolist()
        self.buffer = np.array(self.buffer)
        self.buffer[self.buffer > self.max_buffer_size] = self.max_buffer_size
        self.buffer = self.buffer.tolist()

        FoV_inside_bitrate_each_tile = []
        FoV_adjacency_bitrate_each_tile = []
        FoV_outside_bitrate_each_tile = []
        artificial_range_1s = {}
        range_temp = [0, 1, 2, 3, 4, 5]
        main_perspective = 0
        main_near = []
        for i in range_temp:
            if FoV[self.video_slot + 1][i].astype(int) == 1:
                # count = list(FoV[self.video_slot + 1].astype(int)).count(1)
                artificial_range_1s[i] = artificial_main  # 主视野内权重为0.8
                FoV_inside_bitrate_each_tile.append(action)
                range_temp.remove(i)
                main_perspective = i
                for j in self.FoV_Adjacent[i]:
                    range_temp.remove(j)
                    if FoV[self.video_slot + 1][j].astype(int) == 1:
                        artificial_range_1s[j] = artificial_main_near  # 0.5
                        FoV_adjacency_bitrate_each_tile.append(action)
                        main_near.append(j)
                    else:
                        artificial_range_1s[j] = artificial_not_main  # 0.2
                        FoV_adjacency_bitrate_each_tile.append(action)
        artificial_range_1s[range_temp[0]] = artificial_duimian  ## ???
        FoV_outside_bitrate_each_tile.append(action)

        bitrate_each_tile_weight = []
        for i in range(6):  # 6个面
            bitrate_each_tile_weight.append((action+1) * artificial_range_1s[i])
        bitrate_level_total = np.sum(bitrate_each_tile_weight)
        # 帧内平滑度
        # 考虑FoV的东西，这个平滑度考虑主视角和其他四片的差异
        smoothness_within_frame = 0
        smoothness_inside_FoV = 0
        smoothness_adjacency_FoV = 0
        smoothness_outside_FoV = 0

        bitrate = 0
        for i in range(6):
            bitrate += action

        reward = alpha * bitrate_level_total - beta * smoothness_within_frame - gamma * rebuffer_event
        return [reward, bitrate_level_total, smoothness_within_frame, rebuffer_event, rebuffer_record_time,
                FoV_inside_bitrate_each_tile, FoV_adjacency_bitrate_each_tile, FoV_outside_bitrate_each_tile,
                smoothness_inside_FoV, smoothness_adjacency_FoV, smoothness_outside_FoV]

    def Dynamic_Profit(self,FoV,FoV2,throughput,alpha,beta,gamma,artificial_main,artificial_main_near,artificial_not_main,artificial_duimian): # FoV是未来两个时刻
        if max(self.action)>3:
            print(self.action)
            print("1")
        buffer_current, rebuffer_event,rebuffer_record_time = calculate_buffer(self.action, throughput, self.buffer)
        self.buffer = buffer_current
        self.buffer = np.array(self.buffer)
        self.buffer[self.buffer > self.max_buffer_size] = self.max_buffer_size
        self.buffer = self.buffer.tolist()

        self.action = choose_bitrate_level(value([FoV,FoV2], buffer_current), throughput, FoV)
        if max(self.action)>3:
            print(self.action)
            print("2")
        FoV_inside_bitrate_each_tile = []
        FoV_adjacency_bitrate_each_tile = []
        FoV_outside_bitrate_each_tile = []
        artificial_range_1s = {}
        range_temp = [0, 1, 2, 3, 4, 5]
        main_perspective = 0
        main_near = []
        for i in range_temp:
            if FoV[i].astype(int) == 1:
                # count = list(FoV[self.video_slot + 1].astype(int)).count(1)
                artificial_range_1s[i] = artificial_main  # 主视野内权重为0.8
                FoV_inside_bitrate_each_tile.append(self.action[i])
                range_temp.remove(i)
                main_perspective = i
                for j in self.FoV_Adjacent[i]:
                    range_temp.remove(j)
                    if FoV[j].astype(int) == 1:
                        artificial_range_1s[j] = artificial_main_near  # 0.5
                        FoV_adjacency_bitrate_each_tile.append(self.action[j])
                        main_near.append(j)
                    else:
                        artificial_range_1s[j] = artificial_not_main  # 0.2
                        FoV_adjacency_bitrate_each_tile.append(self.action[j])
        artificial_range_1s[range_temp[0]] = artificial_duimian  ## ???
        FoV_outside_bitrate_each_tile.append(self.action[range_temp[0]])

        # from copy import deepcopy
        bitrate_each_tile_weight = [] # deepcopy(self.action)
        for i in range(6):  # 6个面
            bitrate_each_tile_weight.append((self.action[i]+1) * artificial_range_1s[i])
        bitrate_level_total = np.sum(bitrate_each_tile_weight)
        # 帧内平滑度
        # 考虑FoV的东西，这个平滑度考虑主视角和其他四片的差异
        smoothness_within_frame = 0
        smoothness_inside_FoV = 0
        smoothness_adjacency_FoV = 0
        smoothness_outside_FoV = 0

        for i in self.FoV_Adjacent[main_perspective]:
            if i in main_near:
                smoothness_inside_FoV += abs(self.action[main_perspective] - self.action[i])
                smoothness_within_frame += smoothness_inside_FoV  # abs(action[main_perspective] - action[i])
            else:
                smoothness_adjacency_FoV += abs(0.5 * (self.action[main_perspective] - self.action[i]))
                smoothness_within_frame += smoothness_adjacency_FoV  # abs(0.5 * (action[main_perspective] - action[i]))

        if main_perspective % 2 == 0:
            duimian = main_perspective + 1
        else:
            duimian = main_perspective - 1
        for i in self.FoV_Adjacent[duimian]:
            smoothness_outside_FoV += abs(self.action[duimian] - self.action[i])

        bitrate = 0
        for i in range(6):
            bitrate += self.action[i]
        reward = alpha * bitrate_level_total - beta * smoothness_within_frame - gamma * rebuffer_event
        return [reward, bitrate_level_total, smoothness_within_frame, rebuffer_event, rebuffer_record_time,
                FoV_inside_bitrate_each_tile, FoV_adjacency_bitrate_each_tile, FoV_outside_bitrate_each_tile,
                smoothness_inside_FoV, smoothness_adjacency_FoV, smoothness_outside_FoV]



    def Bola(self,FoV,throughput,alpha,beta,gamma,artificial_main,artificial_main_near,artificial_not_main,artificial_duimian):
        rebuffer_event = 0
        rebuffer_record_time = 0
        for i in range(6):
            am = 0;m=0;
            value, _ = decide_m(self.V, self.rp, self.Q[i],throughput[i])  # 这里的Q是上一时刻的Q,决策出了m

            if value > 0:  # 可以下载后面一个1s的segment
                _, m = decide_m(self.V, self.rp, self.Q[i],throughput[i])
                am = 1
                # print(step)
                Tk = self.state_rate_mapping[m] / throughput[i] # throughput[i]  # Tk，下载时间，也就是Slot持续时间
                self.slot[i] += Tk
                # buffer += 1 - Tk
            else:
                Tk = 1
                self.slot[i] += Tk  # 这个1 是那个不加载时的deta
                # buffer = buffer - Tk

            # 到这里slot结束了，然后计算新的Q
            if self.Q[i] - Tk < 0:
                rebuffer_event += Tk - self.Q[i]
                rebuffer_record_time += 1
            self.Q[i] = max(self.Q[i] - Tk, 0) + am
            self.action[i] = m

        FoV_inside_bitrate_each_tile = []
        FoV_adjacency_bitrate_each_tile = []
        FoV_outside_bitrate_each_tile = []
        artificial_range_1s = {}
        range_temp = [0, 1, 2, 3, 4, 5]
        main_perspective = 0
        main_near = []
        for i in range_temp:
            if FoV[i].astype(int) == 1:
                # count = list(FoV[self.video_slot + 1].astype(int)).count(1)
                artificial_range_1s[i] = artificial_main  # 主视野内权重为0.8
                FoV_inside_bitrate_each_tile.append(self.action[i])
                range_temp.remove(i)
                main_perspective = i
                for j in self.FoV_Adjacent[i]:
                    range_temp.remove(j)
                    if FoV[j].astype(int) == 1:
                        artificial_range_1s[j] = artificial_main_near  # 0.5
                        FoV_adjacency_bitrate_each_tile.append(self.action[j])
                        main_near.append(j)
                    else:
                        artificial_range_1s[j] = artificial_not_main  # 0.2
                        FoV_adjacency_bitrate_each_tile.append(self.action[j])
        artificial_range_1s[range_temp[0]] = artificial_duimian  ## ???
        FoV_outside_bitrate_each_tile.append(self.action[range_temp[0]])

        # from copy import deepcopy
        bitrate_each_tile_weight = []
        for i in range(6):  # 6个面
            bitrate_each_tile_weight.append((self.action[i]+1) * artificial_range_1s[i])
        bitrate_level_total = np.sum(bitrate_each_tile_weight)
        # 帧内平滑度
        # 考虑FoV的东西，这个平滑度考虑主视角和其他四片的差异
        smoothness_within_frame = 0
        smoothness_inside_FoV = 0
        smoothness_adjacency_FoV = 0
        smoothness_outside_FoV = 0

        for i in self.FoV_Adjacent[main_perspective]:
            if i in main_near:
                smoothness_inside_FoV += abs(self.action[main_perspective] - self.action[i])
                smoothness_within_frame += smoothness_inside_FoV  # abs(action[main_perspective] - action[i])
            else:
                smoothness_adjacency_FoV += abs(0.5 * (self.action[main_perspective] - self.action[i]))
                smoothness_within_frame += smoothness_adjacency_FoV  # abs(0.5 * (action[main_perspective] - action[i]))

        if main_perspective % 2 == 0:
            duimian = main_perspective + 1
        else:
            duimian = main_perspective - 1
        for i in self.FoV_Adjacent[duimian]:
            smoothness_outside_FoV += abs(self.action[duimian] - self.action[i])

        bitrate = 0
        for i in range(6):
            bitrate += self.action[i]
        rebuffer_event = abs(rebuffer_event)
        reward = alpha * bitrate_level_total - beta * smoothness_within_frame - gamma * rebuffer_event
        return [reward, bitrate_level_total, smoothness_within_frame, rebuffer_event, rebuffer_record_time,
                FoV_inside_bitrate_each_tile, FoV_adjacency_bitrate_each_tile, FoV_outside_bitrate_each_tile,
                smoothness_inside_FoV, smoothness_adjacency_FoV, smoothness_outside_FoV]

    # BOLA 的Rebuffer

    def Buffer_based(self, FoV, throughput,alpha,beta,gamma,artificial_main,artificial_main_near,artificial_not_main,artificial_duimian):
        reward = 0
        # action = 3
        action = [0,0,0,0,0,0]
        rebuffer_event = 0
        rebuffer_flag = 0
        rebuffer_record_time = 0
        # 2.5 5 7.5
        # 0.5 3 6.4
        # 1 3 5
        # 4 8.5

        for i in range(6):
            if self.buffer[i] <= 2.35:
                action[i] = max(self.before_action_buffer[i]-1,0)
            else: #if self.buffer[i] <= 6.5: # 8.5
                action[i] = min(self.before_action_buffer[i]+1,3)
                #self.before_action_buffer[i]
            #else:
            #    action[i] = min(self.before_action_buffer[i]+1,3)


            if self.buffer[i] < 1:
                self.buffer[i] += throughput[i] / self.state_rate_mapping[action[i]]
                if self.buffer[i] < 1:
                    rebuffer_event += 1 - self.buffer[i]
                    rebuffer_flag = 1
                    rebuffer_record_time += 1
            else:
                self.buffer[i] = 1 + throughput[i] / self.state_rate_mapping[action[i]]
        if not rebuffer_flag:
            self.buffer = (np.array(self.buffer) - 1).tolist()

        self.buffer = np.array(self.buffer)
        self.buffer[self.buffer > self.max_buffer_size] = self.max_buffer_size
        self.buffer = self.buffer.tolist()

        from copy import deepcopy
        self.before_action_buffer = deepcopy(action)

        FoV_inside_bitrate_each_tile = []
        FoV_adjacency_bitrate_each_tile = []
        FoV_outside_bitrate_each_tile = []
        artificial_range_1s = {}
        range_temp = [0, 1, 2, 3, 4, 5]
        main_perspective = 0
        main_near = []
        for i in range_temp:
            if FoV[self.video_slot + 1][i].astype(int) == 1:
                # count = list(FoV[self.video_slot + 1].astype(int)).count(1)
                artificial_range_1s[i] = artificial_main  # 主视野内权重为0.8
                FoV_inside_bitrate_each_tile.append(action[i])
                range_temp.remove(i)
                main_perspective = i
                for j in self.FoV_Adjacent[i]:
                    range_temp.remove(j)
                    if FoV[self.video_slot + 1][j].astype(int) == 1:
                        artificial_range_1s[j] = artificial_main_near  # 0.5
                        FoV_adjacency_bitrate_each_tile.append(action[i])
                        main_near.append(j)
                    else:
                        artificial_range_1s[j] = artificial_not_main  # 0.2
                        FoV_adjacency_bitrate_each_tile.append(action[i])
        artificial_range_1s[range_temp[0]] = artificial_duimian  ## ???
        FoV_outside_bitrate_each_tile.append(action[i])

        bitrate_each_tile_weight = []
        for i in range(6):  # 6个面
            bitrate_each_tile_weight.append((action[i] + 1) * artificial_range_1s[i])
        bitrate_level_total = np.sum(bitrate_each_tile_weight)
        # 帧内平滑度
        # 考虑FoV的东西，这个平滑度考虑主视角和其他四片的差异
        smoothness_within_frame = 0
        smoothness_inside_FoV = 0
        smoothness_adjacency_FoV = 0
        smoothness_outside_FoV = 0

        bitrate = 0
        for i in range(6):
            bitrate += action[i]

        reward = alpha * bitrate_level_total - beta * smoothness_within_frame - gamma * rebuffer_event
        return [reward, bitrate_level_total, smoothness_within_frame, rebuffer_event, rebuffer_record_time,
                FoV_inside_bitrate_each_tile, FoV_adjacency_bitrate_each_tile, FoV_outside_bitrate_each_tile,
                smoothness_inside_FoV, smoothness_adjacency_FoV, smoothness_outside_FoV]

    def Throughtput_based(self, FoV, throughput,alpha,beta,gamma,artificial_main,artificial_main_near,artificial_not_main,artificial_duimian):
        reward = 0
        action = [0,0,0,0,0,0]
        # action = 3
        rebuffer_event = 0
        rebuffer_flag = 0
        rebuffer_record_time = 0
        # 1 4.6 10
        # 1 3 5
        # 0.5 3 6.4
        # 2.4 6.4 16
        # 5 10 18
        # 13 20 28 用的
        for i in range(6):
            if throughput[i] <=6:
                action[i] = 0
            elif throughput[i] <=10:
                action[i] = 1
            elif throughput[i] <=18:
                action[i] = 2
            else:
                action[i] = 3
            if self.buffer[i] < 1:
                self.buffer[i] += throughput[i] / self.state_rate_mapping[action[i]]
                if self.buffer[i] < 1:
                    rebuffer_event += 1 - self.buffer[i]
                    rebuffer_flag = 1
                    rebuffer_record_time += 1
            else:
                self.buffer[i] = 1 + throughput[i] / self.state_rate_mapping[action[i]]
        if not rebuffer_flag:
            self.buffer = (np.array(self.buffer) - 1).tolist()
        self.buffer = np.array(self.buffer)
        self.buffer[self.buffer > self.max_buffer_size] = self.max_buffer_size
        self.buffer = self.buffer.tolist()

        FoV_inside_bitrate_each_tile = []
        FoV_adjacency_bitrate_each_tile = []
        FoV_outside_bitrate_each_tile = []
        artificial_range_1s = {}
        range_temp = [0, 1, 2, 3, 4, 5]
        main_perspective = 0
        main_near = []
        for i in range_temp:
            if FoV[self.video_slot + 1][i].astype(int) == 1:
                # count = list(FoV[self.video_slot + 1].astype(int)).count(1)
                artificial_range_1s[i] = artificial_main  # 主视野内权重为0.8
                FoV_inside_bitrate_each_tile.append(action[i])
                range_temp.remove(i)
                main_perspective = i
                for j in self.FoV_Adjacent[i]:
                    range_temp.remove(j)
                    if FoV[self.video_slot + 1][j].astype(int) == 1:
                        artificial_range_1s[j] = artificial_main_near  # 0.5
                        FoV_adjacency_bitrate_each_tile.append(action[i])
                        main_near.append(j)
                    else:
                        artificial_range_1s[j] = artificial_not_main  # 0.2
                        FoV_adjacency_bitrate_each_tile.append(action[i])
        artificial_range_1s[range_temp[0]] = artificial_duimian  ## ???
        FoV_outside_bitrate_each_tile.append(action[i])

        bitrate_each_tile_weight = []
        for i in range(6):  # 6个面
            bitrate_each_tile_weight.append((action[i] + 1) * artificial_range_1s[i])
        bitrate_level_total = np.sum(bitrate_each_tile_weight)
        # 帧内平滑度
        # 考虑FoV的东西，这个平滑度考虑主视角和其他四片的差异
        smoothness_within_frame = 0
        smoothness_inside_FoV = 0
        smoothness_adjacency_FoV = 0
        smoothness_outside_FoV = 0

        bitrate = 0
        for i in range(6):
            bitrate += action[i]

        reward = alpha * bitrate_level_total - beta * smoothness_within_frame - gamma * rebuffer_event
        return [reward, bitrate_level_total, smoothness_within_frame, rebuffer_event, rebuffer_record_time,
                FoV_inside_bitrate_each_tile, FoV_adjacency_bitrate_each_tile, FoV_outside_bitrate_each_tile,
                smoothness_inside_FoV, smoothness_adjacency_FoV, smoothness_outside_FoV]

    def Pensieve(self):
        pass
